﻿using JMDict.ObjectModel;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Threading.Tasks;
using System.Xml.Linq;
using System.Xml.Serialization;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.Networking.BackgroundTransfer;
using Windows.Storage;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;

// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238

namespace WindowsStoreSampleApp
{
    /// <summary>
    /// An empty page that can be used on its own or navigated to within a Frame.
    /// </summary>
    public sealed partial class MainPage : Page
    {
        private const string jmdicUrl = "ftp://ftp.monash.edu.au/pub/nihongo/JMdict_e.gz";
        private const string dictionaryFileName = "JMdic.xml";

        private DownloadOperation activeDownload;
        private StorageFile zipFile;
        private StorageFile dictionaryFile;

        public static Dictionary Dictionary;

        private DispatcherTimer autoSearchTimer = new DispatcherTimer();

        private Stopwatch sw = new Stopwatch();

        public MainPage()
        {
            this.InitializeComponent();
            this.autoSearchTimer = new DispatcherTimer() { Interval = TimeSpan.FromMilliseconds(300) };            
            this.autoSearchTimer.Tick += autoSearchTimer_Tick;

            this.DataContext = null;
        }

        public string Title
        {
            get
            {
                return "Title";
            }
        }

        void autoSearchTimer_Tick(object sender, object e)
        {
            this.autoSearchTimer.Stop();
            Debug.WriteLine("Ellapsed time in ms = " + sw.ElapsedMilliseconds);

            string searchKeyword = this.SearchTextBox.Text;            

            if (string.IsNullOrWhiteSpace(searchKeyword))
            {
                return;
            }

            if (searchKeyword.Length == 1 && !searchKeyword.StartsWithKanji())
            {
                return;             
            }

            var results = Dictionary.FindEntries(this.SearchTextBox.Text);

            this.Results.Items.Clear();

            if (results.Any())
            {
                foreach (var item in results)
                {
                    this.Results.Items.Add(FormatEntry(item));
                }
            }              
        }

        /// <summary>
        /// Invoked when this page is about to be displayed in a Frame.
        /// </summary>
        /// <param name="e">Event data that describes how this page was reached.  The Parameter
        /// property is typically used to configure the page.</param>
        protected async override void OnNavigatedTo(NavigationEventArgs e)
        {
            try
            {
                this.dictionaryFile = await ApplicationData.Current.LocalFolder.GetFileAsync(dictionaryFileName);

                this.SearchTextBox.IsEnabled = false;
                this.DownloadButton.IsEnabled = false;
                this.DownloadButton.Content = "File already downloaded";
                Dictionary = await InitializeDictionary(this.dictionaryFile);
            }
            catch (FileNotFoundException)
            {
                this.DownloadButton.IsEnabled = true;
            }            
        }

        private async void OnDownloadButtonClick(object sender, RoutedEventArgs e)
        {          
            var uri = new Uri(jmdicUrl);
            var downloader = new BackgroundDownloader();

            this.zipFile = await ApplicationData.Current.LocalFolder.CreateFileAsync("dict.gz", CreationCollisionOption.ReplaceExisting);

            DownloadOperation download = downloader.CreateDownload(uri, this.zipFile);
            
            await StartDownloadAsync(download);

            var unzippedFile = await ApplicationData.Current.LocalFolder.CreateFileAsync(dictionaryFileName, CreationCollisionOption.ReplaceExisting);
                      
            using (var decompressedFileStream = await unzippedFile.OpenStreamForWriteAsync())
            {
                Stream zippedFileStream = await this.zipFile.OpenStreamForReadAsync();
                using (var decompressionStream = new GZipStream(zippedFileStream, CompressionMode.Decompress))
                {
                    await decompressionStream.CopyToAsync(decompressedFileStream);
                }
            }
        }

        private async Task<DownloadOperation> StartDownloadAsync(DownloadOperation downloadOperation)
        {
            this.DownloadButton.IsEnabled = false;
            this.activeDownload = downloadOperation;
            var progress = new Progress<DownloadOperation>(ProgressCallback);

            return await downloadOperation.StartAsync().AsTask(progress);
        }

        private void ProgressCallback(DownloadOperation obj)
        {
            double received = (double)obj.Progress.BytesReceived;
            double total = (double)obj.Progress.TotalBytesToReceive;
            if (double.IsNaN(received) || double.IsNaN(total) || total == 0.0)
            {
                return;
            }

            double progress = received / total;

            DownloadProgress.Value = progress * 100;
            if (progress >= 1.0)
            {
                activeDownload = null;
                DownloadButton.IsEnabled = true;
            }
        }
        private async Task<Dictionary> InitializeDictionary(StorageFile localFile)
        {
            var sw = Stopwatch.StartNew();
            Stream stream = await localFile.OpenStreamForReadAsync();
            var dic = await Task.Run<Dictionary>(() =>  DeSerializer(XElement.Load(stream)));
            Debug.WriteLine("Loading dictionary file took " + sw.ElapsedMilliseconds + "ms. Ready to search.");
            this.SearchTextBox.IsEnabled = true;
            return dic;
        }

        private static Dictionary DeSerializer(XElement element)
        {
            var serializer = new XmlSerializer(typeof(Dictionary));
            return (Dictionary)serializer.Deserialize(element.CreateReader());
        }

        private void OnTextBoxKeyUp(object sender, KeyRoutedEventArgs args)
        {
            if (this.autoSearchTimer.IsEnabled)
            {
                this.autoSearchTimer.Stop();
            }

            sw.Restart();
            this.autoSearchTimer.Start();
        }
        
        private static string FormatEntry(Entry item)
        {
            string formattedEntry = string.Empty;

            foreach (var kanji in item.Kanjis)
            {
                formattedEntry += kanji.Reading + "    ";
            }                     

            foreach (var re in item.ReadingElements)
            {
                formattedEntry += re.Reading + "    ";
            }
                       
            foreach (var sense in item.Senses)
            {
                foreach (var gloss in sense.gloss)
                {
                    foreach (var text in gloss.Text)
                    {
                        formattedEntry += text + ", ";
                    }
                }
            }

            return formattedEntry;
        }
    }
}
